﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Reflection;
using System.Diagnostics;


namespace BlueFlyPhone
{

    [AttributeUsage(AttributeTargets.All)]
    public class KeyAttribute : System.Attribute
    {
        public string Name { get; private set; }

        public bool Required;


        public KeyAttribute(string name)
        {
            this.Name = name;
            Required = false;
        }
        
        public KeyAttribute(string name,bool required)
        {
            this.Name = name;
            Required = required;
        }
    }


    public class FlightRecord : List<IGCFile.BRecord>
    {

        public event EventHandler RecordAdded;

        public new void Add(IGCFile.BRecord record)
        {
            base.Add(record);

            var evt = RecordAdded;

            if (evt != null)
                evt(this, EventArgs.Empty);

        }

    }


    public class IGCFile
    {

        public enum RecordTypes
        {
            A,  // FR manufacturer and identification
            B,  // Fix
            C,  // Task/declaration
            D,  // Differential GPS
            E,  // Event
            F,  // Constellation
            G,  // Security
            H,  // File header
            I,  // List of extension data included at end of each fix B record
            J,  // List of data included in each extension (K) Record
            K,  // Extension data
            L,  // Logbook/comments
            M,  //spare
            N //etc.
        }


        public IGCFile()
        {
            _header = new HeaderRecord();
            _arecord = new ARecord();
            Flight = new FlightRecord();
            Flight.RecordAdded+=Flight_RecordAdded;
        
        }

        void Flight_RecordAdded(object sender, EventArgs e)
        {


                
        }

        HeaderRecord _header;

        public ARecord _arecord;

        public HeaderRecord Header
        {
            get { return _header; }
        }

        public void WriteFile(string path)
        {

            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(ms,Encoding.UTF8))
            {
                WriteARecord(sw,_arecord);
                WriteHeader(sw, _header);

                foreach (var record in this.Flight)
                {
                    record.Write(sw);
                }
            }

            var b = ms.ToArray();
            Debug.WriteLine(System.Text.UnicodeEncoding.UTF8.GetString(b, 0, b.Length));
        }


        public void WriteARecord(System.IO.StreamWriter sw , ARecord record)
        {
            sw.Write(record.Header);
            sw.Write(record.ManufacturerCode);
            sw.WriteLine(record.RecorderDescritpion);
        }



        public void WriteHeader(System.IO.StreamWriter sw, HeaderRecord header)
        {
            sw.WriteRecord(header, c => c.DateRecorded);
            sw.WriteRecord(header, c => c.FixAccuracy);
            sw.WriteRecord(header, c => c.PilotInCharge);
            sw.WriteRecord(header, c => c.SecondPilot);
            sw.WriteRecord(header, c => c.GliderType);
            sw.WriteRecord(header, c => c.GliderId);
            sw.WriteRecord(header, c => c.GpsDatum);
            sw.WriteRecord(header, c => c.FirmwareVersion);
            sw.WriteRecord(header, c => c.HardwareRevisionVersion);
            sw.WriteRecord(header, c => c.ManufacturerAndModel);
            sw.WriteRecord(header, c => c.PressureSensor);
            sw.WriteRecord(header, c => c.CompetitionID);
            sw.WriteRecord(header, c => c.CompetitionClass);
        }



        public FlightRecord Flight
        {
            get;
            private set;
        }


        public class BRecord
        {
            double _rawPressure;
            
            public enum Validity
            {
                A,
                V
            }

            public BRecord(DateTime utc, double rawPressure)
            {
                TimeUTC = utc;
                _rawPressure = rawPressure;
            }


            public BRecord(DateTime utc, double rawPressure, double lat, double lon)
            {
                TimeUTC = utc;
                _rawPressure = rawPressure;
                Lat = lat;
                Lon = lon;
            }


            public BRecord(DateTime utc, double rawPressure, double lat, double lon, double gnssalt)
            {
                TimeUTC = utc;
                _rawPressure = rawPressure;
                Lat = lat;
                Lon = lon;
                
                this.GNSSAlt = gnssalt;
            }


            public string FixValidity
            {
                get { return this.GNSSAlt.HasValue ? "A" : "V"; }
     
            }

            public DateTime TimeUTC
            {
                get;
                set;
            }

            public double? Lat
            {
                get;
                set;
            }

            public double? Lon
            {
                get;
                set;
            }

            public double PressureAlt
            {
                get { return GetPressureAlt(_rawPressure); }
     
            }

            private double GetPressureAlt(double _rawPressure)
            {
                return (1 - Math.Pow((_rawPressure / 101325),  0.190284)) * 145366.45;
            }

            public double? GNSSAlt
            {
                get;
                set;
            }

            public void Write(System.IO.StreamWriter sw)
            {

                sw.Write("B");
                sw.Write(this.TimeUTC.ToUniversalTime().ToString("hhmmss"));

                if (this.Lat.HasValue)
                {
                    sw.Write(toDMS(this.Lat.Value, true));
                    latlast = this.Lat.Value;
                }
                else
                {
                    sw.Write(toDMS(latlast, true));
                }


                if (this.Lon.HasValue)
                {
                    sw.Write(toDMS(this.Lon.Value, false));
                    lnglast = this.Lon.Value;
                }
                else
                {
                    sw.Write(toDMS(lnglast, false));
                }
                
  
                sw.Write(this.FixValidity);

                sw.Write( 

                    this.PressureAlt >= 0 ? this.PressureAlt.ToString("00000") :
                    this.PressureAlt < 0 ? this.PressureAlt.ToString("0000") : null
                   );

                if(this.GNSSAlt.HasValue)
                {
                    sw.Write(this.GNSSAlt.Value.ToString("00000"));
                }

                sw.WriteLine();
            }


            static double latlast;
            static double lnglast;

            string toDMS(double value,bool isLat)
            {

                int sec = (int)Math.Round(value * 3600);
                int deg = sec / 3600;
                sec = Math.Abs(sec % 3600);
                int min = sec / 60;
                sec %= 60;

                string q = isLat && value >= 0 ? "N" :
                    isLat ? "S" :
                    !isLat && value >= 0 ? "E" :
                    "W";


                return isLat ? string.Format("{0:00}{1:00}{2:000}{3}", Math.Abs(deg), min, sec, q) : string.Format("{0:000}{1:00}{2:000}{3}", Math.Abs(deg), min, sec, q);

            }
        
        }

        
        public class ARecord
        {

            public string ManufacturerCode
            {
                get;
                set;
            }

            public string RecorderDescritpion
            {
                get;
                set;
            }
            
            public ARecord()
            {
                ManufacturerCode = "XXX";
            }

            public string Header
            {
                get { return "A"; }
            }

        }

        public class HeaderRecord
        {

            DateTime _date;
            int _fixAccurracy;
            string _pilotIC;
            string _secondPilot;
            string _glidertype;
            string _gliderid;
            string _datum = "WGS-1984";
            string _firmwareVersion = "1.0";
            string _hardwareRevision = "";
            string _manufacturermodel = "";
            string _gpsmodel = "";
            string _pressureSensor = "";
            string _competitionID;
            string _competitionClass;

            [KeyAttribute("HFDTE", true)]
            public string DateRecorded
            {
                get { return _date.ToUniversalTime().ToString("ddMMyy"); }
            }

            [KeyAttribute("HFFXA",true)]
            public string FixAccuracy
            {
                get { return _fixAccurracy.ToString().PadLeft(3, '0'); }
                set { _fixAccurracy = int.Parse(value); }
            }


            [KeyAttribute("HFPLTPILOTINCHARGE:")]
            public string PilotInCharge
            {
                get { return StringExtensions.NonNullTrimmed(_pilotIC); }
                set { _pilotIC = value; }
            }



            [KeyAttribute("HFCM2CREW2:")]
            public string SecondPilot
            {
                get { return StringExtensions.NonNullTrimmed(_secondPilot) ; }
                set { _secondPilot = value; }
            }


            [KeyAttribute("HFGTYGLIDERTYPE:")]
            public string GliderType
            {
                get { return StringExtensions.NonNullTrimmed(_glidertype); }
                set { _glidertype = value; }
            }

            [KeyAttribute("HFGIDGLIDERID:")]
            public string GliderId
            {
                get { return  StringExtensions.NonNullTrimmed(_gliderid); }

              
                set { _gliderid = value; }
            }


            [KeyAttribute("HFDTM100GPSDATUM:")]
            public string GpsDatum
            {
                get { return StringExtensions.NonNullTrimmed(_datum); }
                set { _datum = value; }
            }


            [KeyAttribute("HFRFWFIRMWAREVERSION:")]
            public string FirmwareVersion
            {
                get { return StringExtensions.NonNullTrimmed(_firmwareVersion); }
                set { _firmwareVersion = value; }
            }


            [KeyAttribute("HFRHWHARDWAREVERSION:")]
            public string HardwareRevisionVersion
            {
                get { return StringExtensions.NonNullTrimmed(_hardwareRevision); }
                set { _firmwareVersion = value; }
            }

            [KeyAttribute("HFFTYFRTYPE:")]
            public string ManufacturerAndModel
            {
                get { return StringExtensions.NonNullTrimmed(_manufacturermodel); }
                set { _firmwareVersion = value; }
            }

            [KeyAttribute("HFGPS:")]
            public string GPSModel
            {
                get { return StringExtensions.NonNullTrimmed(_gpsmodel); }
                set { _gpsmodel = value; }
            }


            [KeyAttribute("HFPRSPRESSALTSENSOR:")]
            public string PressureSensor
            {
                get { return StringExtensions.NonNullTrimmed(_pressureSensor); }
                set { _pressureSensor = value; }
            }


            [KeyAttribute("HFCIDCOMPETITIONID:")]
            public string CompetitionID
            {
                get { return StringExtensions.NonNullTrimmed(_competitionID); }
                set { _competitionID = value; }
            }


            [KeyAttribute("HFCCLCOMPETITIONCLASS:")]
            public string CompetitionClass
            {
                get { return StringExtensions.NonNullTrimmed(_competitionClass); }
                set { _competitionClass = value; }
            }

            public static string[] RequiredHeaderSequence = new string[] {
                "HFDTE",
                "HFFXA",
                "HFPLT",
                "HFCM2",
                "HFGTY",
                "HFGID",
                "HFDTM",
                "HFRFW",
                "HFRHW",
                "HFFTY",
                "HFGPS",
                "HFPRS",
                "HFCID",
                "HFCCL"
            };

            public HeaderRecord()
            {
                Data = new Dictionary<string, string>();
                _date = DateTime.Now;
            }

            public Dictionary<string, string> Data
            {

                get;
                private set;
            }
        }
    }

}
